﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using Telerik.Web.UI;
using Medianamik.Core;
using System.Globalization;
using System.Collections.Specialized;
using System.Web.UI.WebControls;
using System.ComponentModel;
using Medianamik.Globalization;

namespace Medianamik.UI.Controls
{
    [ToolboxData("<{0}:MedianamikTreeView runat=server></{0}:MedianamikTreeView>")]
    public class MedianamikTreeView : BaseTemplateControl
    {
        #region Fields

        RadTreeView _tree = new RadTreeView();
        UpdatePanel _panel = new UpdatePanel() { ID = ControlIDs.TreeViewID };
        HiddenField _isNavigating = new HiddenField() { ID = "isNavigating" };
        const string PostBackEventTarget = "__EVENTTARGET";
        const string PostBackEventArgument = "__EVENTARGUMENT";

        #endregion

        #region Events

        void Page_LoadComplete(object sender, EventArgs e)
        {
            var selfAndParents = new List<ITreeItem>();
            //Trouver le Noeud à selectionner
            var currentNode = CurrentItemValue == null
            ? default(RadTreeNode)
            : _tree.FindNodeByValue(CurrentItemValue);
            if (currentNode == null)
            {
                //Charger la hierarchie parents
                var selfAndParentEventArgs = new NeedSelfAndParentsEventArgs(CurrentItemValue);
                OnNeedSelfAndParents(selfAndParentEventArgs);
                selfAndParents.AddRange(selfAndParentEventArgs.Items);
            }

            selfAndParents.Sort((n1, n2) => (n1.Depth ?? 0).CompareTo(n2.Depth ?? 0));
            var parentNodeCol = _tree.Nodes;
            foreach (var treeItem in selfAndParents)
            {
                //Trouver le Noeud
                RadTreeNode node = _tree.FindNodeByValue(treeItem.Value);
                if (node == null)
                {
                    node = BuildTreeNode(treeItem);
                    parentNodeCol.Add(node);

                }
                if (node.Nodes.Count == 0)
                {
                    //Charger les enfants
                    var args = new NeedChildrenEventArgs(treeItem.Value);
                    OnNeedChildren(args);
                    node.Nodes.AddRange(args.ChildNodes.Select(n => BuildTreeNode(n)));
                }
                parentNodeCol = node.Nodes;
            }


            if (CurrentItemValue != null)
            {
                var node = _tree.FindNodeByValue(CurrentItemValue);
                if (node != null)
                {
                    if (node.Nodes.Count == 0)
                    {
                        //Charger les enfants
                        var args = new NeedChildrenEventArgs(CurrentItemValue);
                        OnNeedChildren(args);
                        node.Nodes.AddRange(args.ChildNodes.Select(n => BuildTreeNode(n)));
                    }
                    node.Selected = true;
                    node.Expanded = true;
                    node.ExpandMode = TreeNodeExpandMode.ClientSide;
                    var parentNode = node.ParentNode;
                    while (parentNode != null)
                    {
                        //Désactiver le callback car les noeuds sont déjà chargés
                        parentNode.Expanded = true;
                        parentNode.ExpandMode = TreeNodeExpandMode.ClientSide;
                        parentNode = parentNode.ParentNode;
                    }
                }
            }

            if (NodeToExpandValue != null)
            {
                var node = _tree.FindNodeByValue(NodeToExpandValue);
                if (node != null)
                {
                    if (node.Nodes.Count == 0)
                    {
                        //Charger les enfants
                        var args = new NeedChildrenEventArgs(NodeToExpandValue);
                        OnNeedChildren(args);
                        node.Nodes.AddRange(args.ChildNodes.Select(n => BuildTreeNode(n)));
                    }
                }
                node.Expanded = true;
                node.ExpandMode = TreeNodeExpandMode.ClientSide;
            }

            if (LockEnable)
            {
                _tree.OnClientNodeClicking = "ProcessClientClick";
            }
        }

        protected void NodeExpand(object sender, RadTreeNodeEventArgs e)
        {
            NeedChildrenEventArgs args = new NeedChildrenEventArgs(e.Node.Value);
            OnNeedChildren(args);

            e.Node.Nodes.AddRange(args.ChildNodes.Select(n => BuildTreeNode(n)));
        }

        #region Need Parents Event

        private static readonly object _needSelfAndParentsEvent = new object();
        public event EventHandler<NeedSelfAndParentsEventArgs> NeedSelfAndParents
        {
            add
            {
                Events.AddHandler(_needSelfAndParentsEvent, value);
            }
            remove
            {
                Events.RemoveHandler(_needSelfAndParentsEvent, value);
            }
        }
        protected virtual void OnNeedSelfAndParents(NeedSelfAndParentsEventArgs args)
        {
            var eh = Events[_needSelfAndParentsEvent] as EventHandler<NeedSelfAndParentsEventArgs>;
            if (eh != null)
            {
                eh.Invoke(this, args);
            }
        }

        #endregion

        #region Need Children Event

        private static readonly object _needChildrenEvent = new object();
        public event EventHandler<NeedChildrenEventArgs> NeedChildren
        {
            add
            {
                Events.AddHandler(_needChildrenEvent, value);
            }
            remove
            {
                Events.RemoveHandler(_needChildrenEvent, value);
            }
        }
        protected virtual void OnNeedChildren(NeedChildrenEventArgs args)
        {
            var eh = Events[_needChildrenEvent] as EventHandler<NeedChildrenEventArgs>;
            if (eh != null)
            {
                eh.Invoke(this, args);
            }
        }

        #endregion

        #endregion

        #region Properties

        [Bindable(true)]
        public String NavigateNodeMessage
        {
            get
            {
                object msg = ViewState["NavigateNodeMessage"];
                if (msg == null || string.IsNullOrEmpty(msg.ToString()))
                    return Translator.Translate("medianamik_contentmanagement", "DefaultNavigateNodeMessage");
                return msg.ToString();
            }
            set
            {
                ViewState["NavigateNodeMessage"] = value;
            }
        }

        [Bindable(true)]
        public bool LockEnable
        {
            get
            {
                if (ViewState["LockEnable"] == null)
                    return default(bool);
                return (bool)ViewState["LockEnable"];
            }
            set
            {
                ViewState["LockEnable"] = value;
            }
        }

        public Unit Width
        {
            get
            {
                if (_width == null)
                {
                    EnsureChildControls();
                    return _tree.Width;
                }

                return _width;
            }
            set
            {
                _width = value;
            }
        }
        Unit _width;

        public Unit Height
        {
            get
            {
                if (_height == null)
                {
                    EnsureChildControls();
                    return _tree.Height;
                }

                return _height;
            }
            set
            {
                _height = value;
            }
        }
        Unit _height;

        public string OnClientMouseOut
        {
            get;
            set;
        }

        public string OnClientMouseOver
        {
            get;
            set;
        }

        [Bindable(true)]
        public String CurrentItemValue
        {
            get { return ViewState["CurrentItemValue"] as string; }
            set
            {
                ViewState["CurrentItemValue"] = value;
            }
        }

        [Bindable(true)]
        public String NodeToExpandValue
        {
            get { return ViewState["NodeToExpandValue"] as string; }
            set
            {
                ViewState["NodeToExpandValue"] = value;
            }
        }

        #endregion

        #region Methods

        protected override void CreateChildControls()
        {
            Controls.Add(_panel);
            _panel.ContentTemplate = new TreeViewTemplate(_tree, _isNavigating);
            _tree.NodeExpand += new RadTreeViewEventHandler(NodeExpand);
            Page.LoadComplete += Page_LoadComplete;

            if (LockEnable)
            {
                _tree.OnClientNodeClicking = "ProcessClientClick";
            }

            if (Page.IsPostBack)
            {
                if (!String.IsNullOrEmpty(Page.Request[PostBackEventTarget])
                && Page.Request[PostBackEventTarget].Equals(_isNavigating.ClientID)
                && !String.IsNullOrEmpty(Page.Request[PostBackEventArgument]))
                {
                    AddHistoryPoint(Page.Request[PostBackEventArgument]);
                }
            }
            else
            {
                _tree.Width = Width;
                _tree.Height = Height;

                if (!String.IsNullOrEmpty(OnClientMouseOver))
                    _tree.OnClientMouseOver = OnClientMouseOver;

                if (!String.IsNullOrEmpty(OnClientMouseOut))
                    _tree.OnClientMouseOver = OnClientMouseOut;

                _panel.UpdateMode = UpdatePanelUpdateMode.Conditional;
                _panel.ChildrenAsTriggers = true;
                _tree.OnClientNodeClicked = "ClientNodeClicked";
                _tree.EnableEmbeddedSkins = false;
                _tree.Skin = "Medianamik";
                _tree.LoadingMessage = Translator.Translate("medianik_controls", "treeview_loading_message");
            }
        }

        private void AddHistoryPoint(string historyPoint)
        {
            NameValueCollection pageState = new NameValueCollection();
            pageState.Add("ParentNodeId", historyPoint);
            Page.MyScriptManager.AddHistoryPoint(pageState, historyPoint);
            _isNavigating.Value = "";
        }

        protected RadTreeNode BuildTreeNode(ITreeItem pItem)
        {
            var node = new RadTreeNode(pItem.Text, pItem.Value, pItem.NavigateUrl);

            if (!string.IsNullOrEmpty(pItem.ImageUrl))
            {
                node.ImageUrl = pItem.ImageUrl;
            }

            if ((pItem.HasChildren ?? true))
            {
                node.ExpandMode = TreeNodeExpandMode.ServerSide;
            }

            node.Enabled = pItem.Enabled ?? true;

            if(!pItem.UIEnabled.GetValueOrDefault(true))
            {
                node.Category = "Disabled";
                node.CssClass += " rtDisabled";
                node.HoveredCssClass = "rtDisabled";
                node.SelectedCssClass = "rtDisabled";
            }

            return node;
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            string script = String.Concat("function ClientNodeClicked(sender, eventArgs) {\n",
                " var node = eventArgs.get_node();\n",
                " if (node.get_expandMode() == 1) {\n",
                " var isNavigating = $get('" + _isNavigating.ClientID + "');\n",
                " isNavigating.value = node.get_value();\n",
                " node.expand();\n",
                " }\n",
                " else\n",
                " __doPostBack('" + _isNavigating.ClientID + "', node.get_value());\n",
                " }\n");


            ScriptManager.RegisterClientScriptBlock(this, typeof(TreeView), "MedianamikTreeViewScript",
                    script, true);

            if (LockEnable)
            {
                ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("/Medianamik/Services/LockWebService.asmx"));

                ScriptManager.RegisterClientScriptBlock(this, typeof(MedianamikTreeView), "Confirm",
                    String.Concat("function ProcessClientClick(sender, eventArgs) {",
                                        "eventArgs.set_cancel(!confirm('" + NavigateNodeMessage + "'));",
                                    "}"),
                    true);
            }
        }

        public void Reload()
        {
            _tree.Nodes.Clear();
        }

        #endregion

        #region Inner Classes

        class TreeViewTemplate : ITemplate
        {
            RadTreeView _tree;
            HiddenField _hiddenField;

            public TreeViewTemplate(RadTreeView tree, HiddenField hiddenField)
            {
                _tree = tree;
                _hiddenField = hiddenField;
            }

            public void InstantiateIn(Control container)
            {
                container.Controls.Add(new LiteralControl("<div class=\"BlocArbo\">"));
                container.Controls.Add(_tree);
                container.Controls.Add(new LiteralControl("</div>"));
                if (_hiddenField != null)
                    container.Controls.Add(_hiddenField);
            }
        }

        public class NeedChildrenEventArgs : EventArgs
        {
            public NeedChildrenEventArgs(string pParentValue)
            {
                ParentValue = pParentValue;
            }

            public string ParentValue { get; private set; }

            private List<ITreeItem> _childNodes = new List<ITreeItem>();
            public List<ITreeItem> ChildNodes
            {
                get { return _childNodes; }
            }
        }

        public class NeedSelfAndParentsEventArgs : EventArgs
        {
            public NeedSelfAndParentsEventArgs(string pItemValue)
            {
                ItemValue = pItemValue;
            }

            public string ItemValue { get; private set; }

            private List<ITreeItem> _items = new List<ITreeItem>();
            public List<ITreeItem> Items
            {
                get { return _items; }
            }
        }

        #endregion
    }
}
